using Astar;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

/// <summary>
/// 网格管理器，负责网格的创建和管理
/// </summary>
public class GridManager : MonoBehaviour
{
    public enum BuildAction
    {
        ADD,
        REMOVE
    }
    [Header("网格设置")]
    public int gridWidth = 20;
    public int gridHeight = 20;
    public float cellSize = 1f;
    public Vector3 gridOrigin = Vector3.zero;

    [Header("可视化设置")]
    [SerializeField]private Material gridMaterial;
    [SerializeField]private Color gridColor = Color.white;
    [SerializeField]private bool showGrid = true;
    [SerializeField]private bool showGridInScene = false;
    [Header("导航设置")]
    [SerializeField]bool usePathCache = true;
    public int CurrentFinderCount
    {
        get
        {
            return currentFinderCount;
        }
        set
        {
            currentFinderCount = value;
            if (currentFinderCount < 0)
                currentFinderCount = 0;
        }
    }
    private int currentFinderCount;
    public const int MAX_FINDER_COUNT = 3;

    /// <summary>
    /// 路径长度
    /// </summary>
    /// <returns></returns>
    public class Path
    {
        public Vector3[] nodes;

        public float GetDistanceAlongPath()
        {
            float distance = 0;
            if (nodes == null || nodes.Length <= 1)
            {
                return distance;
            }
            else
            {
                return nodes.Length;
                //for (int index = 0; index < nodes.Length - 1; index++)
                //{
                //    distance += Vector3.Distance(nodes[index], nodes[index + 1]);
                //}
                //return distance;
            }
        }
    }
    public static GridManager Instance { get; private set; }
    public static GridManager instance => Instance;

    public int[,] instanceNodes;//包含实例的节点
    public bool[,] pathNodesWithoutWall;//不考虑墙,穿过墙 true可用 false不可用
    public bool[,] pathNodesWithWall;//不穿墙

    Dictionary<SearchParameters, List<Vector2Int>> pathCacheDetour;
    Dictionary<SearchParameters, List<Vector2Int>> pathCacheCrossWall;
    private const int CACHE_SIZE = 100;

    Dictionary<SearchParameters, System.Action<List<Vector3>>> actionCache;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }

        InitializeGrid();
        CreateGridVisual();
    }
    private void InitializeGrid()
    {
        if (gridWidth <= 0)
            gridWidth = 1;
        if (gridHeight <= 0)
            gridHeight = 1;
        if (cellSize <= 0)
            cellSize = 1;
        this.instanceNodes = new int[gridWidth, gridHeight];
        this.pathNodesWithoutWall = new bool[gridWidth, gridHeight];
        this.pathNodesWithWall = new bool[gridWidth, gridHeight];
        this.pathCacheDetour = new Dictionary<SearchParameters, List<Vector2Int>>();
        this.pathCacheCrossWall = new Dictionary<SearchParameters, List<Vector2Int>>();

        for (int x = 0; x < gridWidth; x++)
        {
            for (int z = 0; z < gridHeight; z++)
            {
                this.instanceNodes[x, z] = -1;
                this.pathNodesWithoutWall[x, z] = true;
                this.pathNodesWithWall[x, z] = true;
            }
        }

       
    }
    public bool CheckGrid(int x, int z)
    {
        if(x >= 0 && x < gridWidth && z >= 0 && z < gridHeight)
        {
            return true;
        }
        return false;
    }
    public void UpdateAllNodes()
    {
        InitializeGrid();
         foreach (KeyValuePair<int, BaseItemScript> entry in SceneManager.instance.GetItemInstances())
        {
            BaseItemScript item = entry.Value;
            if (!item.itemData.configuration.isCharacter)
            {
                this.UpdateBaseItemNodes(item, BuildAction.ADD);
            }
        }
    }

    private void CreateGridVisual()
    {
        if (!showGridInScene) return;

        var gridVisual = new GameObject("GridVisual");
        gridVisual.transform.SetParent(transform);

        LineRenderer lineRenderer = gridVisual.AddComponent<LineRenderer>();
       
        lineRenderer.material = gridMaterial;
        lineRenderer.startColor = gridColor;
        lineRenderer.endColor = gridColor;
        lineRenderer.startWidth = 0.05f;
        lineRenderer.endWidth = 0.05f;
        lineRenderer.useWorldSpace = true;

        // 创建网格线
        int totalLines = gridWidth + gridHeight + 2;
        Vector3[] positions = new Vector3[totalLines * 2];
        int index = 0;

        // 垂直线
        for (int x = 0; x <= gridWidth; x++)
        {
            positions[index++] = new Vector3(x * cellSize, 0, 0) + gridOrigin;
            positions[index++] = new Vector3(x * cellSize, 0, gridHeight * cellSize) + gridOrigin;
        }

        // 水平线
        for (int y = 0; y <= gridHeight; y++)
        {
            positions[index++] = new Vector3(0, 0, y * cellSize) + gridOrigin;
            positions[index++] = new Vector3(gridWidth * cellSize, 0, y * cellSize) + gridOrigin;
        }

        lineRenderer.positionCount = positions.Length;
        lineRenderer.SetPositions(positions);
    }

    /// <summary>
    /// 获取世界坐标对应的网格坐标,方框左下角的点
    /// </summary>
    public Vector2Int GetGridPosition(Vector3 worldPosition)
    {
        Vector3 localPosition = worldPosition - gridOrigin;
        int x = Mathf.FloorToInt(localPosition.x / cellSize);
        int y = Mathf.FloorToInt(localPosition.z / cellSize);

        return new Vector2Int(x, y);
    }


    /// <summary>
    /// 获取网格坐标对应的世界坐标中心点
    /// </summary>
    public Vector3 GetWorldPositionOfGridCenter(int x, int y)
    {
        return new Vector3(x * cellSize + cellSize * 0.5f, 0, y * cellSize + cellSize * 0.5f) + gridOrigin;
        // return new Vector3(x * cellSize, 0, y * cellSize) + gridOrigin;
    }
    /// <summary>
    /// 获取网格坐标对应世界坐标中心点
    /// </summary>
    /// <param name="xy"></param>
    /// <returns></returns>
    public Vector3 GetWorldPositionOfGridCenter(Vector2Int xy)
    {
        return GetWorldPositionOfGridCenter(xy.x, xy.y);
    }
    /// <summary>
    /// 获取网格上的坐标点
    /// </summary>
    public Vector3 GetWorldPositionOnGrid(int x, int y)
    {
        return new Vector3(x * cellSize, 0, y * cellSize) + gridOrigin;
    }
    /// <summary>
    /// 获取网格上的坐标点
    /// </summary>
    /// <param name="worldPosition"></param>
    /// <returns></returns>
    public Vector3 GetWorldPositionOnGrid(Vector3 worldPosition)
    {
        var p = GetGridPosition(worldPosition);
        return new Vector3(p.x * cellSize, 0, p.y * cellSize) + gridOrigin;
    }
    /// <summary>
    /// 获取吸附到网格的世界坐标
    /// </summary>
    /// <param name="xy"></param>
    /// <returns></returns>
    public Vector3 GetWorldPositionOnGrid(Vector2Int xy)
    {
        return GetWorldPositionOnGrid(xy.x, xy.y);
    }
    public Vector3 SnapToGrid(Vector3 worldPosition)
    {
        Vector2Int xy = GetGridPosition(worldPosition);
        xy.x = Mathf.Clamp(xy.x, 0, gridWidth - 1);
        xy.y = Mathf.Clamp(xy.y, 0, gridHeight - 1);
        return GetWorldPositionOnGrid(xy);
    }

    private void OnDrawGizmos()
    {
        if (!showGrid) return;

        Gizmos.color = gridColor;
        for (int x = 0; x < gridWidth; x++)
        {
            for (int y = 0; y < gridHeight; y++)
            {
                // if (instanceNodes[x, y]!=0)
                {
                    Vector3 center = GetWorldPositionOfGridCenter(x, y);
                    Gizmos.DrawWireCube(center, new Vector3(cellSize, 0.1f, cellSize));
                }
            }
        }
        Gizmos.color = Color.blue;
        if (instanceNodes == null)
            return;
        for (int x = 0; x < gridWidth; x++)
        {
            for (int y = 0; y < gridHeight; y++)
            {
                if (instanceNodes[x, y] != -1)
                {
                    Vector3 center = GetWorldPositionOfGridCenter(x, y);
                    Gizmos.DrawWireCube(center, new Vector3(cellSize * .9f, 0.1f, cellSize * .9f));
                }
            }
        }
        Gizmos.color = Color.red;
        for (int x = 0; x < gridWidth; x++)
        {
            for (int y = 0; y < gridHeight; y++)
            {
                if (!pathNodesWithWall[x, y])
                {
                    Vector3 center = GetWorldPositionOfGridCenter(x, y);
                    Gizmos.DrawWireCube(center, new Vector3(cellSize * .9f, 0.1f, cellSize * .7f));
                }
            }
        }
    }
    /// <summary>
    /// 更新基础物品节点
    /// </summary>
    /// <param name="item">Item.</param>
    /// <param name="action">Action.</param>
    public void UpdateBaseItemNodes(BaseItemScript item, BuildAction action)
    {

        Vector3 pos = item.GetPosition();
        int size = (int)item.GetGridSize();
        var grids = GetOccupyGrids(pos, size, size);
        // var outerGrids = GetOuterGrids(pos, size, size);
        // var innerGrids = GetInnerGrids(pos, size, size);

        foreach (var grid in grids)
        {
            int indexX = grid.x;
            int indexZ = grid.y;
            if (!CheckGrid(indexX, indexZ))
                continue;
            
            if (action == BuildAction.ADD)
            {
                //adding scene item to nodes, so walkable is false
                this.instanceNodes[indexX, indexZ] = item.instanceId;

                if (item.itemData.name == "Wall")
                {
                    this.pathNodesWithoutWall[indexX, indexZ] = true;//如果是墙,不考虑墙,可以通过
                }
                else
                {
                    this.pathNodesWithoutWall[indexX, indexZ] = false;//如果不是墙,不能通过
                }

                this.pathNodesWithWall[indexX, indexZ] = false;//考虑墙,不能通过
            }
            else if (action == BuildAction.REMOVE)
            {
                if (this.instanceNodes[indexX, indexZ] == item.instanceId)
                {
                    this.instanceNodes[indexX, indexZ] = -1;
                    this.pathNodesWithoutWall[indexX, indexZ] = true;
                    this.pathNodesWithWall[indexX, indexZ] = true;
                }
            }

        }
        // 更新外围节点可移动
        if (item.itemData.name != "Wall")
        {
            var outerGrids = GetOuterGrids(pos, size, size);
            foreach (var grid in outerGrids)
            {
                pathNodesWithoutWall[grid.x, grid.y] = true;
                pathNodesWithWall[grid.x, grid.y] = true;
            }
        }
        //camp内部可移动
        if (item.itemData.name == "ArmyCamp")
        {
            var innerGrids = GetInnerGrids(pos, size, size);
            foreach (var grid in innerGrids)
            {
                pathNodesWithoutWall[grid.x, grid.y] = true;
                pathNodesWithWall[grid.x, grid.y] = true;
            }
        }
    }
    
    /// <summary>
	/// 获取路径
	/// </summary>
	/// <param name="startPoint">起点</param>
	/// <param name="endPoint">终点</param>
	/// <param name="considerWalls">是否越过障碍</param>
	/// <returns></returns>
	//public Path GetPath(Vector3 startPoint, Vector3 endPoint, bool considerWalls){
	//	Path path = new Path ();

	//	if (endPoint.x < 0 || endPoint.x >= gridWidth || endPoint.z < 0 || endPoint.z >= gridHeight) {
	//		Debug.LogError ("The target point is out of the grid!");
	//		return path;
	//	}

 //       Vector2Int startPointInMap = GetGridPosition(startPoint);
 //       Vector2Int endPointInMap = GetGridPosition(endPoint);
 //       //Debug.Log(string.Format("{0}  {1}  {2}  {3}",startPoint,startPointInMap,endPoint,endPointInMap));
	//	SearchParameters searchParameter = null;

	//	if (considerWalls) {
	//		searchParameter = new SearchParameters (startPointInMap, endPointInMap, pathNodesWithWall);
	//	} else {
	//		searchParameter = new SearchParameters (startPointInMap, endPointInMap, pathNodesWithoutWall);
	//	}
 //       //foreach(var nod in pathNodesWithoutWall)
 //       //{
 //       //    Debug.Log(nod);
 //       //}
 //       PathFinder pathFinder = new PathFinder(searchParameter);
 //       List<Vector2Int> points = pathFinder.FindPath();
 //       //Astar.AStarPath pathFinder = new Astar.AStarPath(searchParameter);
 //       //StartCoroutine(pathFinder.Start(OnPathComplete));

 //       int index = -1;
	//	List<Vector3> nodes = new List<Vector3> ();
	//	foreach (Vector2Int point in points) {
	//		index++;
	//		//Vector3 pointInGround = new Vector3 (point.x, 0, point.y);
 //           Vector3 pointInGround = GetWorldPositionOfGridCenter(point.x, point.y);
	//		nodes.Add (pointInGround);
	//	}
	//	path.nodes = nodes.ToArray ();

	//	return path;
	//}
    public void GetPathSync(Vector3 startPoint, Vector3 endPoint, bool considerWalls,System.Action<List<Vector3>> callback)
    {
        //OnPathComplete = callback;
        //Debug.Log("GetPathSync");
        if (endPoint.x < 0 || endPoint.x >= gridWidth || endPoint.z < 0 || endPoint.z >= gridHeight)
        {
            Debug.LogError("The target point is out of the grid!");
            return;
        }

        Vector2Int startPointInMap = GetGridPosition(startPoint);
        Vector2Int endPointInMap = GetGridPosition(endPoint);

        SearchParameters searchParameter = null;

        if (considerWalls)
        {
            searchParameter = new SearchParameters(startPointInMap, endPointInMap, pathNodesWithWall);
        }
        else
        {
            searchParameter = new SearchParameters(startPointInMap, endPointInMap, pathNodesWithoutWall);
        }
        //System.Action<List<Vector3>> cache;
        //if(actionCache.TryGetValue(searchParameter, out cache))
        //    cache += callback;
        //else
        //actionCache.TryAdd(searchParameter, callback);
        if (usePathCache)
        {
            List<Vector2Int> path;
            if (considerWalls && pathCacheDetour.TryGetValue(searchParameter, out path))
            {
                List<Vector3> nodes = new List<Vector3>();
                
                foreach (var point in path)
                {
                    Vector3 pointInGround = GetWorldPositionOfGridCenter(point.x, point.y);
                    nodes.Add(pointInGround);
                }
                callback(nodes);
                return;
            }
            else if (!considerWalls && pathCacheCrossWall.TryGetValue(searchParameter, out path))
            {
                List<Vector3> nodes = new List<Vector3>();
                foreach (var point in path)
                {
                    Vector3 pointInGround = GetWorldPositionOfGridCenter(point.x, point.y);
                    nodes.Add(pointInGround);
                }
                callback(nodes);
                return;
            }
        }

        System.Action<List<Vector2Int>> OnPathComplete = (points) =>
        {
            List<Vector3> nodes = new List<Vector3>();
            foreach (var point in points)
            {
                Vector3 pointInGround = GetWorldPositionOfGridCenter(point.x, point.y);
                nodes.Add(pointInGround);
            }
            CheckCache();
            if (considerWalls)
                pathCacheDetour.TryAdd(searchParameter, points);
            else
                pathCacheCrossWall.TryAdd(searchParameter, points);
            callback(nodes);
        };
        
        Astar.AStarPath pathFinder = new Astar.AStarPath(searchParameter);
        //pathFinder.Start(OnPathComplete);
        StartCoroutine(pathFinder.Start(OnPathComplete));
    }
    void CheckCache()
    {
        if (pathCacheDetour.Count > CACHE_SIZE)
        {
            pathCacheDetour.Clear();
        }
        if (pathCacheCrossWall.Count > CACHE_SIZE)
        {
            pathCacheCrossWall.Clear();
        }
    }
	public Vector3 GetRandomFreePosition(){
		int x = Random.Range (5, gridWidth-5);
		int z = Random.Range (5, gridHeight-5);

		if (this.instanceNodes [x, z] != -1) {
			return GetRandomFreePosition ();
		}
        return GetWorldPositionOfGridCenter(x, z);
	}

	public Vector3 GetRandomFreePositionForItem(int sizeX, int sizeZ){
        Vector3 randomPosition = new Vector3(Random.Range(0, gridWidth * cellSize), 0, Random.Range(0, gridHeight * cellSize));

		if (!IsPositionPlacable (randomPosition, sizeX, sizeZ, -1)) {
			return GetRandomFreePositionForItem (sizeX, sizeZ);
		}

		return randomPosition;
	}
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sizeX"></param>
    /// <param name="sizeZ"></param>
    /// <returns></returns>
	public Vector3 GetFreePositionForItem(int sizeX, int sizeZ){
		Vector3 freePosition = new Vector3 (-1, 0, -1);

        int p = (int)gridWidth;
		int startX = p / 2;
        int startZ = p / 2;
		for (int r = 0; r < p / 2; r++) {
			for (int x = -r; x <= r; x++) {
				for (int z = -r; z <= r; z++) {
					if (x == -r || x == r || z == -r || z == r) {
                        // freePosition.x = startX + x;
                        // freePosition.z = startZ + z;
                        freePosition = GetWorldPositionOnGrid(startX + x, startZ + z);
						if (IsPositionPlacable(freePosition, sizeX, sizeZ, -1))
                        {
                            return freePosition;
                        }
					}
				}
			}
		}
		return freePosition;
	}
	/// <summary>
	/// 网格是否被占用
	/// </summary>
	/// <param name="position"></param>
	/// <param name="sizeX"></param>
	/// <param name="sizeZ"></param>
	/// <param name="instanceId"></param>
	/// <returns></returns>
	public bool IsPositionPlacable(Vector3 position, int sizeX, int sizeZ, int instanceId){
        Vector2Int pos = GetGridPosition(position);
        //左下角
        int leftX = pos.x - sizeX /2;
        int leftZ = pos.y - sizeZ /2;
        //右上角
        int rightX = leftX + sizeX;
        int rightZ = leftZ + sizeZ;
        if(leftX < 0 || rightX >= gridWidth || leftZ < 0 || rightZ >= gridHeight)
            return false;

        for (int indexX = leftX; indexX < rightX; indexX++)
        {
			for (int indexZ = leftZ; indexZ < rightZ; indexZ++)
            {
				if (instanceNodes [indexX, indexZ] != -1 && instanceNodes [indexX, indexZ] != instanceId)
                {
					return false;
				}
			}
		}
		return true;
	}
    /// <summary>
    /// 获取建筑占用的网格
    /// </summary>
    /// <param name="position"></param>
    /// <param name="sizeX"></param>
    /// <param name="sizeZ"></param>
    /// <returns></returns>
    public List<Vector2Int> GetOccupyGrids(Vector3 position, int sizeX, int sizeZ)
    {
        List<Vector2Int> grids = new List<Vector2Int>();
        Vector2Int pos = GetGridPosition(position);
        //左下角
        int posX = pos.x - sizeX / 2;
        int posZ = pos.y - sizeZ / 2;
        //右上角
        int rightX = posX + sizeX;
        int rightZ = posZ + sizeZ;
        for (int indexX = posX; indexX < rightX; indexX++)
        {
            for (int indexZ = posZ; indexZ < rightZ; indexZ++)
            {
                grids.Add(new Vector2Int(indexX, indexZ));
            }
        }
        return grids;
    }
    /// <summary>
    /// 获取占用网格的边缘
    /// </summary>
    /// <param name="position"></param>
    /// <param name="sizeX"></param>
    /// <param name="sizeZ"></param>
    /// <returns></returns>
    //public List<Vector2Int> GetOuterGridsN(Vector3 position, int sizeX, int sizeZ)
    //{
    //    List<Vector2Int> grids = new List<Vector2Int>();
    //    Vector2Int pos = GetGridPosition(position);//20,7 => 40,14
    //    //X轴
    //    int leftX = pos.x - sizeX / 2;  //36
    //    int rightX = leftX + sizeX;     //44
    //    //Z轴
    //    int downZ = pos.y - sizeZ / 2;  //10
    //    int upZ = downZ + sizeZ;        //18

    //    for (int indexX = leftX; indexX < rightX; indexX++)
    //    {
    //        grids.Add(new Vector2Int(indexX, downZ));//下边
    //        grids.Add(new Vector2Int(indexX, upZ-1));//上边
    //    }
    //    for (int indexP = downZ -1; indexP < upZ -1; indexP++)
    //    {
    //        grids.Add(new Vector2Int(leftX, indexP));//左边
    //        grids.Add(new Vector2Int(rightX-1, indexP));//右边
    //    }
    //    return grids;
    //}
    public List<Vector2Int> GetOuterGrids(Vector3 position, int sizeX, int sizeZ)
    {
        List<Vector2Int> grids = new List<Vector2Int>();
        Vector2Int pos = GetGridPosition(position); // 将世界坐标转换为网格坐标

        // 计算矩形区域的边界（左、右、下、上）
        int leftX = pos.x - sizeX / 2;
        int rightX = leftX + sizeX;
        int downZ = pos.y - sizeZ / 2;
        int upZ = downZ + sizeZ;

        // 1. 添加上边和下边的网格（水平方向）
        // 范围：X从leftX到rightX-1，覆盖整个宽度
        for (int x = leftX; x < rightX; x++)
        {
            grids.Add(new Vector2Int(x, downZ));   // 下边（Z=downZ）
            grids.Add(new Vector2Int(x, upZ - 1)); // 上边（Z=upZ-1，因为upZ是边界的下一个值）
        }

        // 2. 添加左边和右边的网格（垂直方向）
        // 范围：Z从downZ+1到upZ-2（避开上下边已包含的角点，避免重复）
        for (int z = downZ + 1; z < upZ - 1; z++)
        {
            grids.Add(new Vector2Int(leftX, z));    // 左边（X=leftX）
            grids.Add(new Vector2Int(rightX - 1, z)); // 右边（X=rightX-1）
        }

        return grids;
    }
    /// <summary>
    /// 获取 camp 内部路径
    /// </summary>
    /// <param name="position"></param>
    /// <param name="sizeX"></param>
    /// <param name="sizeZ"></param>
    /// <returns></returns>
    public List<Vector2Int> GetInnerGrids(Vector3 position, int sizeX, int sizeZ)
    {
        List<Vector2Int> grids = GetOccupyGrids(position, sizeX - 2, sizeZ - 2);
        Vector2Int pos = GetGridPosition(position);
        int downZ = pos.y - sizeZ / 2;
        grids.Add(new Vector2Int(pos.x, downZ));
        grids.Add(new Vector2Int(pos.x + 1, downZ));
        return grids;
    }
    /// <summary>
    /// 获取网格的物体
    /// </summary>
    /// <param name="position"></param>
    /// <returns></returns>
    public BaseItemScript GetItemInPosition(Vector3 position)
    {
        Vector2Int pos = GetGridPosition(position);
        int posX = pos.x;
        int posZ = pos.y;

        if (posX < 0 || posX >= gridWidth || posZ < 0 || posZ >= gridHeight)
        {
            return null;
        }

        int itemInstanceId = GridManager.Instance.instanceNodes[posX, posZ];
        BaseItemScript item = null;
        SceneManager.instance.GetItemInstances().TryGetValue(itemInstanceId, out item);
        return item;
    }
}
